//    datetime.cpp - This is a modified version of the Date/Time class
//             originally created by Charles Schwarz and Gordon Adams
//             -- Steve Hilla, 1 May 2000.
//
//0005.01, SAH, Change code to use mjd, fmjd as the private data. The
//              difference between two DateTime objects will be in "days".
//0012.27, SAH, Change LeapMonths[] and NormalMonths[] to hold 13 elements.
//0103.13, SAH, modify the >, >=, <, <= manipulator member functions.
//0104.11, SAH, Add an output operator function ( << ).
//0105.08, SAH, add normalize function to prevent HMS output where sec = 60.0
#include "StdAfx.h"
#pragma warning(disable:4273)
#define _CRTDBG_MAP_ALLOC 
#include<stdlib.h> 
#include<crtdbg.h> 

/*
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
*/

#include "GNSSdatetime.h"

#if !defined(MATH_)
#include <math.h>
#define MATH_
#endif

#if !defined(IOSTREAM_)
#include <iostream>
#define IOSTREAM_
#endif

#if !defined(IOMANIP_)
#include <iomanip>
#define IOMANIP_
#endif

namespace NGSS {

   using namespace std;

   const long     JAN61980 = 44244;
   const long     JAN11901 = 15385;

   const long LeapMonths[13]   = { 0,  31,  60,  91, 121, 152, 182,
                                  213, 244, 274, 305, 335, 366 };

   const long NormalMonths[13] = { 0,  31,  59,  90, 120, 151, 181,
                                  212, 243, 273, 304, 334, 365 };

// Constructors

/////////////////////////////////////////////////////////////////////////////
CaDateTime::CaDateTime() { mjd = 0; fractionOfDay = 0.0; }
/////////////////////////////////////////////////////////////////////////////
CaDateTime::CaDateTime( GPSTIME gpstime )
{
   mjd =(long)( gpstime.GPSWeek*7 + gpstime.secsOfWeek/SECPERDAY + JAN61980);
   fractionOfDay = fmod(gpstime.secsOfWeek,SECPERDAY)/SECPERDAY;
}
/////////////////////////////////////////////////////////////////////////////
CaDateTime::CaDateTime( MJD MJD2 )
{
   mjd = MJD2.mjd;
   fractionOfDay = MJD2.fracOfDay;
}
/////////////////////////////////////////////////////////////////////////////
CaDateTime::CaDateTime( YDOYHMS yearDoyHMS )
{
  mjd = ((yearDoyHMS.year - 1901)/4)*1461 + ((yearDoyHMS.year - 1901)%4)*365
         + yearDoyHMS.dayOfYear - 1 + JAN11901;

 fractionOfDay = ((yearDoyHMS.sec/60.0 + yearDoyHMS.min)/60.0 + yearDoyHMS.hour)/24.0;
}
/////////////////////////////////////////////////////////////////////////////
CaDateTime::CaDateTime( YMDHMS yearMonthDayHMS )
{
   long doy;

   if( yearMonthDayHMS.year%4 == 0 )   // good until the year 2100
    doy = LeapMonths[yearMonthDayHMS.month - 1] + yearMonthDayHMS.day;
   else
    doy = NormalMonths[yearMonthDayHMS.month - 1] + yearMonthDayHMS.day;

   mjd = ((yearMonthDayHMS.year - 1901)/4)*1461 + ((yearMonthDayHMS.year -
           1901)%4)*365 + doy - 1 + JAN11901;

   fractionOfDay = ((yearMonthDayHMS.sec/60.0 + yearMonthDayHMS.min)/60.0 +
           yearMonthDayHMS.hour)/24.0;
}
/////////////////////////////////////////////////////////////////////////////
CaDateTime::CaDateTime( long year, long month, long day,
                    long hour, long min, double sec )
{
   long doy;

   if( year%4 == 0 )   // good until the year 2100
    doy = LeapMonths[month - 1] + day;
   else
    doy = NormalMonths[month - 1] + day;

   mjd = ((year - 1901)/4)*1461 + ((year -
           1901)%4)*365 + doy - 1 + JAN11901;

   fractionOfDay = ((sec/60.0 + min)/60.0 + hour)/24.0;
}
/////////////////////////////////////////////////////////////////////////////
// Destructor
CaDateTime::~CaDateTime()
{
   // no need to delete mjd and fractionOfDay
   // since they were not created using new.
}
// Initializers - used to set the value of an existing variable.
/////////////////////////////////////////////////////////////////////////////
void CaDateTime::SetGPSTime( GPSTIME gpstime2 )
{
   *this = CaDateTime( gpstime2 );
}
/////////////////////////////////////////////////////////////////////////////
void CaDateTime::SetMJD( MJD inputMJD )
{
   *this = CaDateTime( inputMJD );
}
/////////////////////////////////////////////////////////////////////////////
void CaDateTime::SetYDOYHMS( YDOYHMS yearDoyHMS )
{
   *this = CaDateTime( yearDoyHMS );
}
/////////////////////////////////////////////////////////////////////////////
void CaDateTime::SetYMDHMS( YMDHMS yearMonthDayHMS )
{
   *this = CaDateTime( yearMonthDayHMS );
}
/////////////////////////////////////////////////////////////////////////////
void CaDateTime::SetYMDHMS( long year, long month, long monthday,
                          long hour, long min, double sec )
{
   *this = CaDateTime( year, month, monthday, hour, min, sec );
}
/////////////////////////////////////////////////////////////////////////////
// Selectors - return the contents of a variable.
GPSTIME CaDateTime::GetGPSTime()
{
   GPSTIME tt;

   tt.GPSWeek    = (mjd - JAN61980)/7;
   tt.secsOfWeek = ( (mjd - JAN61980) - tt.GPSWeek*7 +
                     fractionOfDay)*SECPERDAY;
   return tt;
}
/////////////////////////////////////////////////////////////////////////////
MJD CaDateTime::GetMJD()
{
   MJD mm;

   mm.mjd  = mjd;
   mm.fracOfDay = fractionOfDay;

   return mm;
}
/////////////////////////////////////////////////////////////////////////////
YDOYHMS CaDateTime::GetYDOYHMS()
{
   YDOYHMS     yy;
   long   daysFromJan11901, deltaYears, numberFourYears;
   long   yearsSoFar, daysLeft;

   daysFromJan11901 = mjd - JAN11901;
   numberFourYears = daysFromJan11901/1461;
   yearsSoFar = 1901 + 4*numberFourYears;
   daysLeft = daysFromJan11901 - 1461*numberFourYears;
   deltaYears = daysLeft/365 - daysLeft/1460;

   yy.year = yearsSoFar + deltaYears;
   yy.dayOfYear = daysLeft - 365*deltaYears + 1;
   yy.hour =(LONG) fractionOfDay*24.0;
   yy.min = (LONG)(fractionOfDay*1440.0 - yy.hour*60.0);
   yy.sec = fractionOfDay*86400.0 - yy.hour*3600.0 - yy.min*60.0;

   return yy;
}
/////////////////////////////////////////////////////////////////////////////
YMDHMS CaDateTime::GetYMDHMS()
{
   YMDHMS   ymd;
   long   daysFromJan11901, deltaYears, numberFourYears;
   long   more, guess, doy, yearsSoFar, daysLeft;

   daysFromJan11901 = mjd - JAN11901;
   numberFourYears = daysFromJan11901/1461;
   yearsSoFar = 1901 + 4*numberFourYears;
   daysLeft = daysFromJan11901 - 1461*numberFourYears;
   deltaYears = daysLeft/365 - daysLeft/1460;

   ymd.year = yearsSoFar + deltaYears;
   doy = daysLeft - 365*deltaYears + 1;
   int hour = fractionOfDay*24.0;
   ymd.hour = (LONG)hour;
   ymd.min = (LONG)(fractionOfDay*1440.0 - ymd.hour*60.0);
   ymd.sec = fractionOfDay*86400.0 - ymd.hour*3600.0 - ymd.min*60.0;

   guess = (LONG)doy*0.032;
   more = 0;
   if( ymd.year%4 == 0 )   // good until the year 2100
   {
    if( (doy - LeapMonths[guess+1]) > 0 ) more = 1;
    ymd.month = guess + more + 1;
    ymd.day = doy - LeapMonths[guess+more];
   }
   else
   {
    if( (doy - NormalMonths[guess+1]) > 0 ) more = 1;
    ymd.month = guess + more + 1;
    ymd.day = doy - NormalMonths[guess+more];
   }

   return ymd;
}
/////////////////////////////////////////////////////////////////////////////
// Operators
CaDateTime* CaDateTime::operator&(CaDateTime &input)
{
   return &input;
}
/////////////////////////////////////////////////////////////////////////////
// const return avoids: (a1 = a2 ) = a3
// return reference avoids the copy
const CaDateTime &CaDateTime::operator=(const CaDateTime &DT2)
{
   if( &DT2 != this ) // avoids self assignment
   {
     mjd = DT2.mjd;
     fractionOfDay = DT2.fractionOfDay;
   }
   return *this;
}
/////////////////////////////////////////////////////////////////////////////
CaDateTime CaDateTime::operator + ( const double days )  // DT + "d.fff"
{
   CaDateTime   DT;
   DT.mjd           = mjd;
   DT.fractionOfDay = fractionOfDay + days;

   if( DT.fractionOfDay > 1.0 )
   {
       double wholeDays = floor( DT.fractionOfDay );     // use normjd *******
       DT.mjd = DT.mjd + (long) wholeDays;
       DT.fractionOfDay = DT.fractionOfDay - wholeDays;
   }
   // return the copy of temporary struct DT!
   return DT;
}
/////////////////////////////////////////////////////////////////////////////
double CaDateTime::operator - ( const CaDateTime &DT2 ) // find difference in days
// this method will allow a negative difference
{
   return (  ( ((double)mjd - (double)DT2.mjd) + fractionOfDay ) -
               DT2.fractionOfDay );
}
/////////////////////////////////////////////////////////////////////////////
bool CaDateTime::operator == ( const CaDateTime &DT2 )
{
   return ( mjd == DT2.mjd  &&  fractionOfDay == DT2.fractionOfDay );
}
/////////////////////////////////////////////////////////////////////////////
bool CaDateTime::operator != ( const CaDateTime &DT2 )
{
   return ( mjd != DT2.mjd  ||  fractionOfDay != DT2.fractionOfDay );
}
/////////////////////////////////////////////////////////////////////////////
bool CaDateTime::operator > ( const CaDateTime &DT2 )
{
   if ( mjd > DT2.mjd  ||
        (mjd == DT2.mjd  &&  fractionOfDay > DT2.fractionOfDay) )
   {
      return true;
   }
   return false;
}
/////////////////////////////////////////////////////////////////////////////
bool CaDateTime::operator >= ( const CaDateTime &DT2 )
{
   if ( mjd > DT2.mjd  ||
        (mjd == DT2.mjd  &&  fractionOfDay >= DT2.fractionOfDay)  )
   {
      return true;
   }
   return false;
}
/////////////////////////////////////////////////////////////////////////////
bool CaDateTime::operator < ( const CaDateTime &DT2 )
{
   if ( mjd < DT2.mjd  ||
        (mjd == DT2.mjd  &&  fractionOfDay < DT2.fractionOfDay) )
   {
      return true;
   }
   return false;
}
/////////////////////////////////////////////////////////////////////////////
bool CaDateTime::operator <= ( const CaDateTime &DT2 )
{
   if ( mjd < DT2.mjd  ||
        (mjd == DT2.mjd  &&  fractionOfDay <= DT2.fractionOfDay) )
   {
      return true;
   }
   return false;
}
//////////////////////////////////////////////////////////////////////////
CString CaDateTime::ToString(void)
{
	YMDHMS ymdhms = GetYMDHMS();
	CString str;
	str.Format("%4d-%d-%d %d:%d:%0.2f",ymdhms.year,ymdhms.month,ymdhms.day,
				ymdhms.hour,ymdhms.min,ymdhms.sec);
	return str;
}
/////////////////////////////////////////////////////////////////////////////
ostream &operator<<( ostream &output, CaDateTime &dt )
{

	YMDHMS ymdhms = dt.GetYMDHMS();
	output.setf( ios::fixed, ios::floatfield );
	output	<< setw(4) << ymdhms.year << "-" ;

	if (ymdhms.month<10)
		output 	<< "0"<< setw(1) << ymdhms.month<< "-";
	else output << setw(2) << ymdhms.month << "-";

	output 	<< setw(2) << ymdhms.day << "," ;
		
	if (ymdhms.hour<10)
		output 	<< "0"<< setw(1) << ymdhms.hour << ":" ;
	else output << setw(2) << ymdhms.hour << ":" ;

	if (ymdhms.min<10)
		output 	<< "0"<< setw(1) << ymdhms.min << ":" ;
	else output << setw(2) << ymdhms.min << ":" ;

	if (ymdhms.sec<10-0.001)
		output 	<< "0" << setw(1) << setprecision(0)<< ymdhms.sec ;
	else output << setw(2) << setprecision(0)<< ymdhms.sec ;

	return output;      // enables chaining
}

} // namespace 
